// --*- C++ -*------x---------------------------------------------------------
// $Id: AtomCode.h,v 1.3 2007-10-26 14:55:13 biocomp Exp $
//
// Class:           -
// 
// Base class:      -
//
// Derived classes: - 
//
// Author:          Silvio Tosatto, Matthias Heiler
//
// Project name:    Victor
//
// Date:            10/98, 08/99
//
// Description:     Translator: PDB names to internal one-word-code and 
//                  vice versa. Provides some simple predicates dealing with 
//                  one-word-code. 
//
// NB: AtomTranslator converts some "illegal" entries (eg. 1HD, 2HE, ...)
// to their "legal" equivalent (eg. HD, HE, ...)
//
// Copyright:       This file contains information from the Bioinformatics 
//                  Template Library (BTL).
//
//                  Copyright (C) 1997,1998 Birkbeck College, Malet Street, 
//                  London WC1E 7HX, U.K. (classlib@mail.cryst.bbk.ac.uk)
//
// -----------------x-------------------x-------------------x-----------------

#ifndef __ATOM_CODE_H__
#define __ATOM_CODE_H__

// Includes

#include <string>
#include <Debug.h>

/** Internal one-word-code for PDB names.

    This table is taken from the Bioinformatics Template Library (BTL).

    The order of this table is the standard order in which atoms of an
    AminoAcid are stored. The idea is that if you point at atom i then
    all atoms with indices > i "follow" atom[i] in a natural sense. Of
    course there are some special cases, but in general I think this
    is a good idea. ;-)

    The default AtomCode X is moved to the very back of this list.

    @author Matthias Heiler using BTL */
enum AtomCode 
{ 
  // Backbone atoms
  N,   
  HN,  
  CA,  
  HA,  
  HA1, 
  HA2, 
  C,   
  O,   
  // Side chain atoms
  CB,    
  HB,  
  HB1, 
  HB2, 
  HB3, 
  SG,  
  OG,  
  CG,  
  OG1, 
  CG1, 
  CG2, 
  HG,  
  HG11,
  HG12,
  HG13,
  HG21,
  HG22,
  HG23,
  CD,  
  OD,  
  SD,  
  CD1, 
  OD1, 
  ND1, 
  CD2, 
  OD2, 
  ND2, 
  HD,  
  HD1, 
  HD2, 
  HD11,
  HD12,
  HD13,
  HD21,
  HD22,
  HD23,
  CE,  
  NE,  
  CE1, 
  OE1, 
  NE1, 
  CE2, 
  OE2, 
  NE2, 
  CE3, 
  HE,  
  HE1, 
  HE2, 
  HE3, 
  CZ,  
  NZ,  
  CZ2, 
  CZ3, 
  OH,  
  NH1, 
  NH2, 
  CH2, 
  OXT, 
  P,   
  O1P, 
  O2P, 
  O5S, 
  C5S, 
  C4S, 
  O4S, 
  C3S, 
  O3S, 
  C2S, 
  O2S, 
  C1S, 
  N9,  
  C8,  
  N7,  
  C5,  
  C6,  
  O6,  
  N6,  
  N1,  
  C2,  
  O2,  
  N2,  
  N3,  
  C4,  
  O4,  
  N4,  
  C5M, 
  H,   
  HH,  
  HH1, // insterted on 12/11/99
  HH2,
  HZ, 
  HZ1, // taken out because of problems with enum with more than 100 entries.
  HZ2,
  HZ3,
  // side chain pseudo atoms:
  XA,
  XR,
  XN,
  XD,
  XC,
  XQ,
  XE,
  XG,
  XH,
  XI,
  XL,
  XK,
  XM,
  XF,
  XP,
  XS,
  XT,
  XW,
  XY,
  XV,

  X,     //  Please leave as last element! Corresponds to unknown atom type 
  ATOM_CODE_SIZE // number of atom types
};

// ---------------------------------------------------------------------------
//                                AtomTranslator
// -----------------x-------------------x-------------------x-----------------

/** Is second behind first in amino acid? This predicate is used to
    determine if atom second is to be moved if atom first changed its
    position. 

    NOT COMPLETE YET! SPECIAL HANDLING FOR SIDE CHAIN REQUIRED! */
inline
bool
follows(AtomCode first, AtomCode second)
{
  if (first == ATOM_CODE_SIZE || second == ATOM_CODE_SIZE)
    {
      return false;
    }
  /*
  else if (first == C) // C is followed only by O.
    {
      return second == O;
    }
  else if (first == O) // O is followed by nobody.
    {
      return false;
    }
  */
  else
    {
      return first < second;
    }
}

///check if atom is side chain beta atom
inline
bool 
isBetaAtom(AtomCode code) 
{
    return ( code == CB  );
}

///check if atom is side chain gamma atom
inline
bool 
isGammaAtom(AtomCode code) 
{
    return (( code == SG  ) || ( code == OG ) || ( code == CG )
	    || ( code == OG1 ) || ( code == CG1 ) || ( code == CG2 ));
}

///check if atom is side chain delta atom
inline
bool 
isDeltaAtom(AtomCode code) 
{
    return (( code == CD  ) || ( code == OD ) || ( code == SD )
	    || ( code == CD1 ) || ( code == OD1 ) || ( code == ND1 )
	    || ( code == CD2 ) || ( code == OD2 ) || ( code == ND2 ));
}

///check if atom is side chain epsilon atom
inline
bool 
isEpsilonAtom(AtomCode code) 
{
    return (( code == CE  ) || ( code == NE ) || ( code == CE1 )
	    || ( code == OE1 ) || ( code == NE1 ) || ( code == CE2 )
	    || ( code == OE2 ) || ( code == NE2 ) || ( code = CE3 ));
}

///check if atom is side chain zeta atom
inline
bool 
isZetaAtom(AtomCode code) 
{
    return (( code == CZ  ) || ( code == NZ ) || ( code == CZ2 )
	    || ( code == CZ3 ));
}

///check if atom is side chain eta atom
inline
bool 
isEtaAtom(AtomCode code) 
{
    return (( code == OH  ) || ( code == NH1 ) || ( code == NH2 )
	    || ( code == CH2 ));
}

/** is atom a C atom ?  */
// To be verified! 
inline
bool 
isCAtom(AtomCode code) 
{
  return (( code == C   ) || ( code == CB  ) || ( code == CG  ) 
	  || ( code == CG1 ) || ( code == CG2 ) || ( code == CD  ) 
	  || ( code == CD1 ) || ( code == CD2 ) || ( code == CE  ) 
	  || ( code == CE1 ) || ( code == CE2 ) || ( code == CE3 ) 
	  || ( code == CZ  ) || ( code == CZ2 ) || ( code == CZ3 ) 
	  || ( code == C5S ) || ( code == C4S ) || ( code == C3S ) 
	  || ( code == C2S ) || ( code == C1S ) || ( code == C8  ) 
	  || ( code == C5  ) || ( code == C6  ) || ( code == C2  ) 
	  || ( code == C4  ) || ( code == C5M ) );
}

/** is atom a C atom ?  */
// To be verified! 
inline
bool 
isNAtom(AtomCode code) 
{
  return ( (code == N9) || (code == N7) || (code == N6)
	   || (code == N1) || (code == N2) || (code == N3)
	   || (code == N4) || (code == ND1) || (code == ND2)
	   || (code == NE) || (code == NE1) || (code == NE2)
	   || (code == NZ) || (code == NH1) || (code == NH2)
	   || (code == N) );
}

/** is atom a C atom ?  */
// To be verified! 
inline
bool 
isOAtom(AtomCode code) 
{
  return ( (code == OXT) || (code == O1P) || (code == O2P)
	   || (code == O5S) || (code == O4S) || (code == O3S)
	   || (code == O2S) || (code == O6) || (code == O2)
	   || (code == O4) || (code == OD1) || (code == OD2)
	   || (code == OE1) || (code == OE2) || (code == OH)
	   || (code == O) || (code == OG) || (code == OG1)
	   || (code == OD) );  
}

/** is code specifying an H atom ? */
// To be verified! 
inline
bool 
isHAtom(AtomCode code) 
{
  return (code == H || code == HA || code == HB || code == HG
	  || code == HD  || code == HE || code == HE1
	  || code == HE2 || code == HE3 || code == HH 
	  || code == HH1 || code == HH2
	  || code == HN
	  || code == HA1 || code == HA2 || code == HB1 || code == HB2 
	  || code == HB3 || code == HG11 || code == HG12 || code == HG13 
	  || code == HG21 || code == HG22 || code == HG23 || code == HD1  
	  || code == HD2 || code == HD11 || code == HD12 || code == HD13 
	  || code == HD21 || code == HD22 || code == HD23 || code == HZ
	  || code == HZ1 || code == HZ2 || code == HZ3
	  );
}

/**
   true, if atom type name is known 
   magic code X corresponds to an unknown atom code
   @author Eckart Bindewald
   @date 03/1999
*/
inline
bool
isKnownAtom(AtomCode code)
{
  return !(code == X);
}


/** Is code a non-H atom? */
inline
bool
isHeavyAtom(AtomCode code)
{
  return (isKnownAtom(code))&&(!isHAtom(code));
}

// To be verified! 
inline
bool 
isBackboneAtom(AtomCode code) 
{
  return code == C || code == CA || code == N || code == O || code == OXT;
}

/** Translate string into atom code enum. */
inline
AtomCode
AtomTranslator(const string& name)
{
  DUMP(name);
  if (name == "")
    {
      return X;
    }
  if (name == "X")
    {
      return X;
    }
  if (name == "N")
    {
      return N;
    }
  else if (name == "CA")
    {
      return CA;
    }
  else if (name == "C")
    {
      return C;
    }
  else if (name == "O")
    {
      return O;
    }
  else if (name == "CB")
    {
      return CB;
    }
  else if (name == "SG")
    {
      return SG;
    }
  else if (name == "OG")
    {
      return OG;
    }
  else if (name == "CG")
    {
      return CG;
    }
  else if (name == "OG1")
    {
      return OG1;
    }
  else if (name == "CG1")
    {
      return CG1;
    }
  else if (name == "CG2")
    {
      return CG2;
    }
  else if (name == "CD")
    {
      return CD;
    }
  else if (name == "OD")
    {
      return OD;
    }
  else if (name == "SD")
    {
      return SD;
    }
  else if (name == "CD1")
    {
      return CD1;
    }
  else if (name == "OD1")
    {
      return OD1;
    }
  else if (name == "ND1")
    {
      return ND1;
    }
  else if (name == "CD2")
    {
      return CD2;
    }
  else if (name == "OD2")
    {
      return OD2;
    }
  else if (name == "ND2")
    {
      return ND2;
    }
  else if (name == "CE")
    {
      return CE;
    }
  else if (name == "NE")
    {
      return NE;
    }
  else if (name == "CE1")
    {
      return CE1;
    }
  else if (name == "OE1")
    {
      return OE1;
    }
  else if (name == "NE1")
    {
      return NE1;
    }
  else if (name == "CE2")
    {
      return CE2;
    }
  else if (name == "OE2")
    {
      return OE2;
    }
  else if (name == "NE2")
    {
      return NE2;
    }
  else if (name == "CE3")
    {
      return CE3;
    }
  else if (name == "CZ")
    {
      return CZ;
    }
  else if (name == "NZ")
    {
      return NZ;
    }
  else if (name == "CZ2")
    {
      return CZ2;
    }
  else if (name == "CZ3")
    {
      return CZ3;
    }
  else if (name == "OH")
    {
      return OH;
    }
  else if (name == "NH1")
    {
      return NH1;
    }
  else if (name == "NH2")
    {
      return NH2;
    }
  else if (name == "CH2")
    {
      return CH2;
    }
  else if (name == "OXT")
    {
      return OXT;
    }
  else if (name == "P")
    {
      return P;
    }
  else if (name == "O1P")
    {
      return O1P;
    }
  else if (name == "O2P")
    {
      return O2P;
    }
  else if (name == "O5S")
    {
      return O5S;
    }
  else if (name == "C5S")
    {
      return C5S;
    }
  else if (name == "C4S")
    {
      return C4S;
    }
  else if (name == "O4S")
    {
      return O4S;
    }
  else if (name == "C3S")
    {
      return C3S;
    }
  else if (name == "O3S")
    {
      return O3S;
    }
  else if (name == "C2S")
    {
      return C2S;
    }
  else if (name == "O2S")
    {
      return O2S;
    }
  else if (name == "C1S")
    {
      return C1S;
    }
  else if (name == "N9")
    {
      return N9;
    }
  else if (name == "C8")
    {
      return C8;
    }
  else if (name == "N7")
    {
      return N7;
    }
  else if (name == "C5")
    {
      return C5;
    }
  else if (name == "C6")
    {
      return C6;
    }
  else if (name == "O6")
    {
      return O6;
    }
  else if (name == "N6")
    {
      return N6;
    }
  else if (name == "N1")
    {
      return N1;
    }
  else if (name == "C2")
    {
      return C2;
    }
  else if (name == "O2")
    {
      return O2;
    }
  else if (name == "N2")
    {
      return N2;
    }
  else if (name == "N3")
    {
      return N3;
    }
  else if (name == "C4")
    {
      return C4;
    }
  else if (name == "O4")
    {
      return O4;
    }
  else if (name == "N4")
    {
      return N4;
    }
  else if (name == "C5M")
    {
      return C5M;
    }
  else if ( (name == "H") || (name == "1H") 
	    || (name == "2H") || (name == "3H") )
    {
      return H;
    }
  else if (name == "HA")
    {
      return HA;
    }
  else if (name == "HB") // 1HB, 2HB & 3HB are treated below
    {
      return HB;
    }
  else if ( (name == "HG") || (name == "1HG") || (name == "2HG") 
	    || (name == "3HG") )
    {
      return HG;
    }
  else if ( (name == "HD") || (name == "1HD") || (name == "2HD") 
	    || (name == "3HD") )
    {
      return HD;
    }
  else if ( (name == "HE") || (name == "1HE") || (name == "2HE") 
	    || (name == "3HE") )
    {
      return HE;
    }
  else if (name == "HE1")
    {
      return HE1;
    }
  else if ((name == "HE2") || (name == "1HE2") || (name == "2HE2"))  
    {
      return HE2;
    }
  else if (name == "HE3")
    {
      return HE3;
    }
  else if ( (name == "HH") || (name == "1HH") || (name == "2HH") 
	    || (name == "3HH") ) // don't know if it exists, for completeness
    {
      return HH;
    }
  else if ( (name == "HH1") || (name == "1HH1") || (name == "2HH1") )
    {
      return HH1;
    }
  else if ( (name == "HH2") || (name == "1HH2") || (name == "2HH2") )
    {
      return HH2;
    }
  else if (name == "HN")
    {
      return HN;
    }
  else if (name == "1HA")
    {
      return HA1;
    }
  else if (name == "2HA")
    {
      return HA2;
    }
  else if (name == "1HB")
    {
      return HB1;
    }
  else if (name == "2HB")
    {
      return HB2;
    }
  else if (name == "3HB")
    {
      return HB3;
    }
  else if ( (name == "1HG1") || (name == "HG1") )
    {
      return HG11;
    }
  else if ( (name == "2HG1") || (name == "HG2") )
    {
      return HG12;
    }
  else if ( (name == "3HG1") || (name == "HG3") )
    {
      return HG13;
    }
  else if (name == "1HG2")
    {
      return HG21;
    }
  else if (name == "2HG2")
    {
      return HG22;
    }
  else if (name == "3HG2")
    {
      return HG23;
    }
  else if (name == "HD1")
    {
      return HD1;
    }
  else if (name == "HD2")
    {
      return HD2;
    }
  else if (name == "1HD1")
    {
      return HD11;
    }
  else if (name == "2HD1")
    {
      return HD12;
    }
  else if (name == "3HD1")
    {
      return HD13;
    }
  else if (name == "1HD2")
    {
      return HD21;
    }
  else if (name == "2HD2")
    {
      return HD22;
    }
  else if (name == "3HD2")
    {
      return HD23;
    }

  else if ( (name == "HZ") || (name == "1HZ") || (name == "2HZ") 
	    || (name == "3HZ") )
    {
      return HZ;
    }
  else if (name == "HZ1")
    {
      return HZ1;
    }
  else if (name == "HZ2")
    {
      return HZ2;
    }
  else if (name == "HZ3")
    {
      return HZ3;
    }

  // side chain pseudo atoms:
  else if (name == "XA")
    {
      return XA;
    }
  else if (name == "XD")
    {
      return XD;
    }
  else if (name == "XR")
    {
      return XR;
    }
  else if (name == "XN")
    {
      return XN;
    }
  else if (name == "XC")
    {
      return XC;
    }
  else if (name == "XQ")
    {
      return XQ;
    }
  else if (name == "XE")
    {
      return XE;
    }
  else if (name == "XG")
    {
      return XG;
    }
  else if (name == "XG")
    {
      return XG;
    }
  else if (name == "XH")
    {
      return XH;
    }
  else if (name == "XI")
    {
      return XI;
    }
  else if (name == "XL")
    {
      return XL;
    }
  else if (name == "XK")
    {
      return XK;
    }
  else if (name == "XM")
    {
      return XM;
    }
  else if (name == "XF")
    {
      return XF;
    }
  else if (name == "XP")
    {
      return XP;
    }
  else if (name == "XS")
    {
      return XS;
    }
  else if (name == "XT")
    {
      return XT;
    }
  else if (name == "XW")
    {
      return XW;
    }
  else if (name == "XY")
    {
      return XY;
    }
  else if (name == "XV")
    {
      return XV;
    }

  return X; // atom type name is unknown
}

inline
string
AtomTranslator(AtomCode code) 
{
  switch (code)
    {
    case X:
      return "X";
    case N:
      return "N";
    case CA:
      return "CA";
    case C:
      return "C";
    case O:
      return "O";
    case CB:
      return "CB";
    case SG:
      return "SG";
    case OG:
      return "OG";
    case CG:
      return "CG";
    case OG1:
      return "OG1";
    case CG1:
      return "CG1";
    case CG2:
      return "CG2";
    case CD:
      return "CD";
    case OD:
      return "OD";
    case SD:
      return "SD";
    case CD1:
      return "CD1";
    case OD1:
      return "OD1";
    case ND1:
      return "ND1";
    case CD2:
      return "CD2";
    case OD2:
      return "OD2";
    case ND2:
      return "ND2";
    case CE:
      return "CE";
    case NE:
      return "NE";
    case CE1:
      return "CE1";
    case OE1:
      return "OE1";
    case NE1:
      return "NE1";
    case CE2:
      return "CE2";
    case OE2:
      return "OE2";
    case NE2:
      return "NE2";
    case CE3:
      return "CE3";
    case CZ:
      return "CZ";
    case NZ:
      return "NZ";
    case CZ2:
      return "CZ2";
    case CZ3:
      return "CZ3";
    case OH:
      return "OH";
    case NH1:
      return "NH1";
    case NH2:
      return "NH2";
    case CH2:
      return "CH2";
    case OXT:
      return "OXT";
    case P:
      return "P";
    case O1P:
      return "O1P";
    case O2P:
      return "O2P";
    case O5S:
      return "O5S";
    case C5S:
      return "C5S";
    case C4S:
      return "C4S";
    case O4S:
      return "O4S";
    case C3S:
      return "C3S";
    case O3S:
      return "O3S";
    case C2S:
      return "C2S";
    case O2S:
      return "O2S";
    case C1S:
      return "C1S";
    case N9:
      return "N9";
    case C8:
      return "C8";
    case N7:
      return "N7";
    case C5:
      return "C5";
    case C6:
      return "C6";
    case O6:
      return "O6";
    case N6:
      return "N6";
    case N1:
      return "N1";
    case C2:
      return "C2";
    case O2:
      return "O2";
    case N2:
      return "N2";
    case N3:
      return "N3";
    case C4:
      return "C4";
    case O4:
      return "O4";
    case N4:
      return "N4";
    case C5M:
      return "C5M";
    case H:
      return "H";
    case HA:   
      return "HA";
    case HB:  
      return "HB";
    case HG:  
      return "HG";
    case HD:  
      return "HD";
    case HE:  
      return "HE";
    case HE1: 
      return "HE1";
    case HE2: 
      return "HE2";
    case HE3: 
      return "HE3";
    case HH:  
      return "HH";
    case HH1: 
      return "HH1";
    case HH2: 
      return "HH2";
    case HN:  
      return "HN";
    case HA1: 
      return "1HA";
    case HA2: 
      return "2HA";
    case HB1: 
      return "1HB";
    case HB2: 
      return "2HB";
    case HB3: 
      return "3HB";
    case HG11:
      return "1HG1";
    case HG12:
      return "2HG1";
    case HG13:
      return "3HG1";
    case HG21:
      return "1HG2";
    case HG22:
      return "2HG2";
    case HG23:
      return "3HG2";
    case HD1: 
      return "HD1";
    case HD2: 
      return "HD2";
    case HD11:
      return "1HD1";
    case HD12:
      return "2HD1";
    case HD13:
      return "3HD1";
    case HD21:
      return "1HD2";
    case HD22:
      return "2HD2";
    case HD23:
      return "3HD2";
    case HZ:
      return "HZ";
    case HZ1:
      return "HZ1";
    case HZ2:
      return "HZ2";
    case HZ3:
      return "HZ3";
      // side chain pseudo atoms:
    case XA:
      return "XA";
    case XR:
      return "XR";
    case XN:
      return "XN";
    case XD:
      return "XD";
    case XC:
      return "XC";
    case XQ:
      return "XQ";
    case XE:
      return "XE";
    case XG:
      return "XG";
    case XH:
      return "XH";
    case XI:
      return "XI";
    case XL:
      return "XL";
    case XK:
      return "XK";
    case XM:
      return "XM";
    case XF:
      return "XF";
    case XP:
      return "XP";
    case XS:
      return "XS";
    case XT:
      return "XT";
    case XW:
      return "XW";
    case XY:
      return "XY";
    case XV:
      return "XV";


    case ATOM_CODE_SIZE: 
      ERROR("AtomTranslator(AtomCode code): unknown code", exception);
    }

  ERROR("AtomTranslator(AtomCode code): unknown code", exception);

  return "X";
}

inline
ostream&
operator<<(ostream& os, const AtomCode& rval)
{
  os << AtomTranslator(rval);
  return os;
}

inline
istream&
operator>>(istream& is, AtomCode& rval)
{
  string name;
  is >> name;
  rval = AtomTranslator(name);
  return is;
}

#endif /* __ATOM_CODE_H__ */

